import threading
import time
import traceback

import pymysql
from utils.LogUtil import LogUtil

mutex = threading.Lock()


class MySqlDb:
  """MySql 数据帮助类
  """

  def __init__(self, setting={}) -> None:
    self.setting = setting
    self.__conn = None
    self.__cursor = None
    self.__reconnect = True # 连接超时是否重新连接
    self.__retryMaxNum = 3  # 最大重试次数
    if len(setting) > 0:
      self.initDB()

  # 此类被回收时同时关闭数据库连接
  def __del__(self):
    """此类被回收时同时关闭数据库连接
    """
    if self.__conn:
      self.__conn.close()

  #region 帮助方法

  # 初始化数据库
  def initDB(self, setting={}):
    """初始化数据库

    Args:
        setting (dict, optional): _description_. Defaults to {}.

    Returns:
        r (bool): true 数据库连接打开成功, false 数据库连接打开失败
    """
    if len(setting) > 0:
      self.setting = setting
    if len(self.setting) < 1:
      print(f'请填写数据库相关信息配置!!!')
      return False
    r = self.__open()
    return r

  # 获取最后插入的 id 值,没有则返回影响行数
  def getLastRowId(self):
    """获取最后插入的 id 值,没有则返回影响行数

    Returns:
        id (int): 最后插入的 id 值
    """
    self.__asset()
    id = self.__cursor.lastrowid if self.__cursor.rowcount >= 0 else self.__cursor.rowcount
    return id

  #endregion

  #region 私有方法

  # 数据库服务重连
  def __ping(self):
    """数据库服务重连
    """
    retryManNum = self.__retryMaxNum
    retryNum = 0
    while self.__reconnect and retryManNum > retryNum:
      try:
        if not self.__conn or not self.__conn.open:
          self.__open()
          print(f'数据库连接重试次数: {retryNum+1}')
          if self.__conn and self.__conn.open:
            break
      except:
        traceback.print_exc()
      retryNum += 1
      time.sleep(1)

  # 开启一个数据库连接
  def __open(self):
    """开启一个数据库连接

    Returns:
        r (bool): true 数据库连接打开成功, false 数据库连接打开失败
    """
    try:
      self.__conn = pymysql.connect(**self.setting)
    except Exception as ex:
      traceback.print_exc()
      # raise ex
      return False
    self.__cursor = self.__conn.cursor()
    return True

  #endregion

  #region 核心执行语句

  # 参数诊断
  def __asset(self):
    """参数诊断
    """
    # print( self.__conn is not None, self.__cursor is not None)
    assert self.__conn is not None, '__conn 对象未初始化,请先初始化数据库'
    assert self.__cursor is not None or not self.__cursor, '__cursor 对象未初始化'

  # 执行语句并返回最后修改那行的id
  def __executeBackRowId(self, sql, data):
    """执行语句并返回最后受影响一行的id

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据

    Returns:
        rowid (int): 最后受影响一行的id
    """
    self.__execute(sql, data)
    return self.getLastRowId()

  # 执行语句并返回收到影响的行数
  def __executeRowNum(self, sql, data):
    """执行语句并返回受影响的行数

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据

    Returns:
        rowcount (int): 受影响的行数
    """
    self.__execute(sql, data)
    return self.__cursor.rowcount

  # 仅执行语句
  def __execute(self, sql, data):
    """仅执行语句

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据
    """
    self.__asset()
    self.__ping()
    try:
      mutex.acquire()
      self.__cursor.execute(sql, data)
      self.__conn.commit()
      mutex.release()
    except Exception as ex:
      if LogUtil:
        LogUtil.error(ex, f"SQL执行失败:[ {sql} ]")
      else:
        traceback.print_exc()
      if self.__conn and self.__conn.open:
        self.__conn.rollback()

  #endregion

  #region 增删改查 CURD

  # 执行插入语句并返回最后插入行的id
  def insert(self, sql, data):
    """执行插入语句并返回最后插入行的id

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据

    Returns:
        rowid (int): 最后受影响一行的id
    """
    return self.__executeBackRowId(sql, data)

  # 执行删除语句并返回修改的行数
  def delete(self, sql, data):
    """执行删除语句并返回修改的行数

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据

    Returns:
        rowcount (int): 受影响的行数
    """
    return self.__executeRowNum(sql, data)

  # 执行更新语句并返回修改的行数
  def update(self, sql, data):
    """执行更新语句并返回修改的行数

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据

    Returns:
        rowcount (int): 受影响的行数
    """
    return self.__executeRowNum(sql, data)

  # 执行查询语句并返回获取到的数据
  def query(self, sql, data=None):
    """执行查询语句并返回获取到的数据

    Args:
        sql (str): sql语句
        data (tuple | list | dict): 数据

    Returns:
        rows (dict): 查询结果
    """
    try:
      self.__cursor.execute(sql, data)
    except:
      traceback.print_exc()()
      self.__conn.rollback()
    data = self.__cursor.fetchall()
    return data

  #endregion
